Contents page

Rules for Tools/Example Tool


Example Tool

Now that we've discussed all the critical features of Tools and ToolMasters, let's write a short Tool and see first hand how this all works. For our example, we'll use the Flip Tool. This takes all Note Events that enter it and flips them around an axis:
#include "bars.h" #include <libraries/dos.h> #include <proto/exec.h> #include
<exec/memory.h> #include <string.h> #include <intuition/intuition.h>

/*   Four letter identifier. */ #define ID_FLIP 0x464C4950

/*  Image data for the Flip Tool. Make sure this is loaded into CHIP memory. */

static UWORD flip[] = {
    /*------ plane # 0: --------*/
    0x0, 0x0, 0x18, 0x0, 0x18, 0x0,
    0x18, 0x0, 0x18, 0x0, 0x18, 0x0,
    0x18, 0x0, 0x18, 0x0, 0x18, 0x0,
    0x18, 0x0, 0x18, 0x0, 0x0, 0x0,
    /*------ plane # 1: --------*/
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x200, 0x4000, 0xc700, 0x4300, 0xcf83, 0x5b00,
    0xdac1, 0xf300, 0xc200, 0xe300, 0x200, 0x4000,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    /*------ plane # 2: --------*/
    0x0, 0x0, 0xfff, 0xf000, 0x3fff, 0xfc00,
    0x3fff, 0xfc00, 0x3fff, 0xfc00, 0x3fff, 0xfc00,
    0x3fff, 0xfc00, 0x3fff, 0xfc00, 0x3fff, 0xfc00,
    0x3fff, 0xfc00, 0xfff, 0xf000, 0x0, 0x0 };

struct  Image flipimage = {
    0,0,24,12,3,
    &flip[0],0x1f,0x00,NULL, };

/* The Flip Tool structure, a normal Tool structure
   with the flip axis added. */ struct FlipTool {
    struct Tool tool;
    short axis; };

/* Access the functions global structure pointer.  This
   is located and initialized in toolstart.c */ extern struct Functions *functions;

/* This routine defines the behavior of the Flip Tool.
   If the Event is a MIDI Note On, Off or Poly After-Touch
   Event, it flips the note around the axis, so ascending
   scales become descending scales and visa versa.
   To make the effect more musical, Flip does this in the
   current key, using the library routines twelvetoscale()
   and scaletotwelve() to translate into key and then back
   into MIDI.  For a complete description of these routines,
   read up on them in the library summary. */ static struct Event
*processeventcode(event) struct NoteEvent *event; {
    struct FlipTool *tool = (struct FlipTool *) event->tool;
    short value;
    short center;
    char offset, offset2;

    /* Check if the Tool has been initialized.  */

    if (!tool->tool.touched) {
        tool->axis = 60;
        tool->tool.touched = TOUCH_INIT;
    }

    /* Point the Event to the next Tool in the PipeLine. */

    event->tool = event->tool->next;

    /* Check if this is a Note or Poly After-Touch Event. */

    if ((event->status == MIDI_NOTEON) ||
        (event->status == MIDI_NOTEOFF) ||
        (event->status == MIDI_PTOUCH)) {
        /* Convert from MIDI to scale. Offset holds the accidental. */
        value = (*functions->twelvetoscale)
                (tool->tool.clip,event->time,event->value,&offset);

        /* Convert the Flip axis to scale as well. */
        center = (*functions->twelvetoscale)
                (tool->tool.clip,event->time,tool->axis,&offset2);

        /* Flip. */
        value = center - value;
        value = center + value;

        /* Convert back to MIDI. */
        value = (*functions->scaletotwelve)
                (tool->tool.clip,event->time,value);

        /* Subtract the accidental (to go other way.)  */
        value -= offset;

        /* Make sure the result is within MIDI range. */
        while (value < 0) value += 12;
        while (value > 127) value -= 12;

        /* Done, place value in Event. */
        event->value = value;
        }

    /* Return the Event.  THIS IS IMPORTANT!  */
    return(event); }

/* The Flip control window source code is stored in the
   file "flipw.c."  It's a standard Intuition window
   description created with Power Windows. */ extern struct NewWindow
flipNewWindowStructure1;

/* When the user double clicks on the Flip icon in a
   PipeLine, Bars&Pipes creates a Task and calls
   this routine from within the Task.  If your Tool
   does not require a Control Window
   (CounterPoint and UnStick don't, for example,)
   you need not provide this.

   This routine opens an Intuition Window, processes
   user input, and returns after receiving a CLOSEWINDOW
   command.  Notice that the routine is completely reentrant
   so multiple windows can be opened at the same time for
   multiple Tools. */

void edittoolcode(tool) struct FlipTool *tool; {
    struct IntuiMessage *message;
    struct Window *window;
    long class, code;
    short octave, key;
    char string[10];
    static struct IntuiText itext = { 7,0,JAM2,0,0,NULL,0,0 };
    static char *convert[] = {
        "C ","C#","D ","D#",
        "E ","F ","F#","G ",
        "G#","A ","A#","B "
    };
    struct Gadget *gadget;
    struct NewWindow *newwindow;

    /* This is a custom screen, so get screen pointer. */
    flipNewWindowStructure1.Screen = functions->screen;

    /* If Tool has been edited before, set Window parameters. */
    if (tool->tool.touched & TOUCH_EDIT) {
        flipNewWindowStructure1.LeftEdge = tool->tool.left;
        flipNewWindowStructure1.TopEdge = tool->tool.top;
        flipNewWindowStructure1.Width = tool->tool.width;
        flipNewWindowStructure1.Height = tool->tool.height;
    }

    /* Check if Tool is uninitialized. */
    if (!tool->tool.touched) tool->axis = 60;

    /* Call InovaTools routine to duplicate window. */
    newwindow = (struct NewWindow *)
        (*functions->DupeNewWindow)(&flipNewWindowStructure1);
    if (!newwindow) return;
    window = (struct Window *) OpenWindow(newwindow);
    if (!window) return;
    tool->tool.window = window;
    for (;;) {
        /* Refresh window. */
        itext.IText = convert[tool->axis % 12];
        PrintIText(window->RPort,&itext,82,21);
        sprintf(string,"%ld",tool->axis / 12);
        itext.IText = string;
        PrintIText(window->RPort,&itext,120,21);

        message = GetMsg(window->UserPort);
        while (!message) {
            WaitPort(window->UserPort);
            message = GetMsg(window->UserPort);
        }

        class = message->Class;
        code = message->Code;
        gadget = (struct Gadget *) message->IAddress;
        ReplyMsg((struct Message *)message);

        if (class == CLOSEWINDOW) break;
        else if (class == GADGETDOWN) {
            class = gadget->GadgetID;

            switch (class) {
                case 1 :

                /* Use the Bars&Pipes library routine that opens a popup menu
                   of piano keys. */
                    key = (*functions->popupkey)(window,tool->axis % 12);
                    octave = tool->axis / 12;
                    code = (octave * 12) + key;
                    tool->axis = code;
                    break;
                case 2 :
                /* Use the Bars&Pipes library routine that opens a popup menu
                   of octaves. */
                    octave =
                        (*functions->popupoctave)(window,tool->axis / 12);
                    key = tool->axis % 12;
                    code = (octave * 12) + key;
                    tool->axis = code;
                    break;
            }
        }
    }

/* When done, be sure to place the window parameters in the
   Tool and clear the window field so Bars&Pipes knows this
   Tool does not have a window open. */

    tool->tool.window = 0;
    tool->tool.left = window->LeftEdge;
    tool->tool.top = window->TopEdge;
    tool->tool.width = window->Width;
    tool->tool.height = window->Height;
    tool->tool.touched = TOUCH_INIT | TOUCH_EDIT;
    CloseWindow(window);
    (*functions->DeleteNewWindow)(newwindow); }

/* The ToolMaster structure for this Tool. */ static struct ToolMaster master;

/* This routine is called when Bars&Pipes first loads your
   Tool.  It should initialize the ToolMaster structure as
   well as any other structures, interrupts, etc., particular
   to your Tool.

   Notice that there is no "main" routine in this program. */

struct ToolMaster * inittoolmaster() {
    memset((char *)&master,0,sizeof(struct ToolMaster));
    master.toolid = ID_FLIP;
    master.image = &flipimage;
    strcpy(master.name,"Flip");
    master.edittool = edittoolcode;
    master.processevent = processeventcode;
    master.tooltype = TOOL_NORMAL;
    master.toolsize = sizeof(struct FlipTool);
    return(&master); }

   That's it!

   To compile "flip.c" and the window file, "flipw.c," with Lattice, type:

     lc -y -ad -v flip lc flipw

   then Blink with the following link file:

     FROM toolstart.o,flip.o,flipw.o TO flip.ptool LIB lib:amiga.lib,lib:lc.lib

   There is no special library that you need to link with because all Bars&Pipes
routines are accessed directly through the functions pointer (which is, by the way,
faster than the traditional Amiga library C interface.)

   Be sure, if you are compiling with a data address base register enabled, to compile
all routines that are called from Bars&Pipes so that registers other than D0, D1, A0,
and A1 are left intact and the base register is loaded.  The geta4() function in both
Lattice and Aztec solves this problem.

   Aztec users, compile and link as follows:

     cc +B +L +C +D flip.c cc +B +L +C +D flipw.c ln +Cd -O flip.ptool toolstart.o
flipw.o flip.o -l cl32

   Aztec users also need to recompile `toolstart.c'.  This module replaces the normal
Aztec or Lattice startup code.  It initializes system library pointers as well as the
Functions pointer for Bars&Pipes, then calls your inittoolmaster() routine.  Use
`toolstart.o' for both Tools and Accessories.

   Since it's very short, here's the source code to `toolstart.c':

     #include "bars.h"

long IntuitionBase; long GfxBase; long LayersBase; long DOSBase; long SysBase;

struct Functions *functions;

struct ToolMaster * start(f) struct Functions *f; {
    functions = f;
    SysBase = functions->SysBase;
    DOSBase = functions->DOSBase;
    LayersBase = functions->LayersBase;
    GfxBase = functions->GfxBase;
    IntuitionBase = functions->IntuitionBase;
    return((struct ToolMaster *) inittoolmaster()); }

   Notice that, in addition to the normal DOS and System libraries, the Intuition,
Layers, and Graphics libraries are already opened for your Tool;  you don't have to
open them yourself.